//
//  PSHandTests.m
//  PrefCore
//
//  Created by Sergey on 2/10/14.
//  Copyright (c) 2014 Play-on-smart. All rights reserved.
//

#import <XCTest/XCTest.h>
#import "PSHand.h"
#import "PSGame.h"

@interface PSHandTests : XCTestCase

@end

@implementation PSHandTests

-(void)testAddCardToHand {
    for (PSCardIndex i = 1 ; i <= PS_CARD_SUIT_RISING_LEN*PS_CARD_RANK_COUNT ; i++) {
        PSHandHash hand = 0;
        PSCard *card = [PSCard cardWithIndex:i];
        hand = PSHandAddCard(hand, card);
        XCTAssertNotEqual(hand, 0);
        XCTAssertEqual(1, PSHandGetSize(hand));
    }
}

-(void)testGetSuit {
    for (PSCardIndex i = 1 ; i <= PS_CARD_SUIT_RISING_LEN*PS_CARD_RANK_COUNT ; i++) {
        PSHandHash hand = 0;
        PSCard *card = [PSCard cardWithIndex:i];
        hand = PSHandAddCard(hand, card);
        for (int i = 0 ; i != PS_CARD_SUIT_RISING_LEN ; i++) {
            PSCardSuit suit = PSCardSuitRising[i];
            PSCardRankSet cardsInSuit = PSHandGetSuit(hand, suit);
            if (card.suit == suit) {
                XCTAssertNotEqual(0, cardsInSuit & (1 << card.rank));
                XCTAssertEqual(0, cardsInSuit & ~(1 << card.rank));
            } else {
                XCTAssertEqual(0, cardsInSuit);
            }
        }
    }
}

-(void)testKeepSuit {
    for (PSCardIndex i = 1 ; i <= PS_CARD_SUIT_RISING_LEN*PS_CARD_RANK_COUNT ; i++) {
        PSHandHash hand = 0;
        PSCard *card = [PSCard cardWithIndex:i];
        hand = PSHandAddCard(hand, card);
        for (int i = 0 ; i != PS_CARD_SUIT_RISING_LEN ; i++) {
            PSCardSuit suit = PSCardSuitRising[i];
            PSHandHash cardsInSuit = PSHandKeepSuit(hand, suit);
            if (card.suit == suit) {
                XCTAssertNotEqual(0, cardsInSuit & (1 << (card.rank + PSCardSuitPower(suit))));
                XCTAssertEqual(0, cardsInSuit & ~(1 << (card.rank + PSCardSuitPower(suit))));
            } else {
                XCTAssertEqual(0, cardsInSuit);
            }
        }
    }
}

-(void)testAddHand {
    PSHandHash one = PSHandAddCard(0, [PSCard cardWithIndex:23]);
    PSHandHash two = PSHandAddCard(0, [PSCard cardWithIndex:19]);
    PSHandHash res = PSHandAddHand(one, two);
    for (PSCardIndex i = 1 ; i <= PS_CARD_SUIT_RISING_LEN*PS_CARD_RANK_COUNT ; i++) {
        BOOL check = PSHandContainsCard(res, [PSCard cardWithIndex:i].hash32);
        if (i == 19 || i == 23) {
            XCTAssertTrue(check);
        } else {
            XCTAssertFalse(check);
        }
    }
}

-(void)testAddCard {
    PSHandHash res = PSHandAddCard(0, [PSCard cardWithIndex:7]);
    for (PSCardIndex i = 1 ; i <= PS_CARD_SUIT_RISING_LEN*PS_CARD_RANK_COUNT ; i++) {
        BOOL check = PSHandContainsCard(res, [PSCard cardWithIndex:i].hash32);
        if (i == 7) {
            XCTAssertTrue(check);
        } else {
            XCTAssertFalse(check);
        }
    }
}

-(void)testAddCards {
    PSCardHash cards[3];
    cards[0] = [PSCard cardWithIndex:5].hash32;
    cards[1] = [PSCard cardWithIndex:7].hash32;
    cards[2] = [PSCard cardWithIndex:11].hash32;
    PSHandHash res = PSHandAddCards(0, cards, 3);
    for (PSCardIndex i = 1 ; i <= PS_CARD_SUIT_RISING_LEN*PS_CARD_RANK_COUNT ; i++) {
        BOOL check = PSHandContainsCard(res, [PSCard cardWithIndex:i].hash32);
        if (i == 5 || i == 7 || i == 11) {
            XCTAssertTrue(check);
        } else {
            XCTAssertFalse(check);
        }
    }
}

-(void)testAddSuitFromSet {
    PSCardRankSet spades = kMaskAce | kMaskKing | kMaskNine;
    PSCardRankSet clubs = kMaskQueen | kMaskTen | kMaskSeven;
    PSCardRankSet diamonds = kMaskAce | kMaskJack;
    PSCardRankSet hearts = kMaskQueen;
    PSHandHash res = 0;
    res = PSHandAddSuitFromSet(res, spades, kSuitSpades);
    res = PSHandAddSuitFromSet(res, clubs, kSuitClubs);
    res = PSHandAddSuitFromSet(res, diamonds, kSuitDiamonds);
    res = PSHandAddSuitFromSet(res, hearts, kSuitHearts);
    XCTAssertEqualObjects(@"♠: A, K, 9 ♣: Q, 10, 7 ♦: A, J ♥: Q", PSHandDescription(res));
}

-(void)testAddSuitFromHand {
    PSHandHash other = PSHandAddCard(0, [PSCard cardWithSuit:kSuitClubs andRank:kRankSeven]);
    other = PSHandAddCard(other, [PSCard cardWithSuit:kSuitClubs andRank:kRankJack]);
    other = PSHandAddCard(other, [PSCard cardWithSuit:kSuitClubs andRank:kRankQueen]);
    other = PSHandAddCard(other, [PSCard cardWithSuit:kSuitSpades andRank:kRankQueen]);
    other = PSHandAddCard(other, [PSCard cardWithSuit:kSuitDiamonds andRank:kRankQueen]);
    other = PSHandAddCard(other, [PSCard cardWithSuit:kSuitHearts andRank:kRankQueen]);
    PSHandHash res = PSHandAddCard(0, [PSCard cardWithSuit:kSuitClubs andRank:kRankAce]);
    XCTAssertEqual(1, PSHandGetSize(res));
    res = PSHandAddSuit(res, kSuitClubs, other);
    XCTAssertEqual(4, PSHandGetSize(res));
}

-(void)testRemoveCard {
    PSHandHash hand = PSHandAddCard(0, [PSCard cardWithSuit:kSuitClubs andRank:kRankSeven]);
    hand = PSHandAddCard(hand, [PSCard cardWithSuit:kSuitClubs andRank:kRankJack]);
    hand = PSHandAddCard(hand, [PSCard cardWithSuit:kSuitClubs andRank:kRankQueen]);
    hand = PSHandAddCard(hand, [PSCard cardWithSuit:kSuitSpades andRank:kRankQueen]);
    hand = PSHandAddCard(hand, [PSCard cardWithSuit:kSuitDiamonds andRank:kRankQueen]);
    hand = PSHandAddCard(hand, [PSCard cardWithSuit:kSuitHearts andRank:kRankQueen]);
    XCTAssertEqual(6, PSHandGetSize(hand));
    PSHandHash other;
    // Remove a card that is in the hand
    other = PSHandAddCard(0, [PSCard cardWithSuit:kSuitSpades andRank:kRankQueen]);
    hand = PSHandRemoveHand(hand, other);
    XCTAssertEqual(5, PSHandGetSize(hand));
    // Remove a card that is not in the hand
    PSHandHash before = hand;
    other = PSHandAddCard(0, [PSCard cardWithSuit:kSuitHearts andRank:kRankAce]);
    hand = PSHandRemoveHand(hand, other);
    XCTAssertEqual(before, hand);
}

-(void)testRemoveSuit {
    PSHandHash hand = PSHandAddCard(0, [PSCard cardWithSuit:kSuitClubs andRank:kRankSeven]);
    hand = PSHandAddCard(hand, [PSCard cardWithSuit:kSuitClubs andRank:kRankJack]);
    hand = PSHandAddCard(hand, [PSCard cardWithSuit:kSuitClubs andRank:kRankQueen]);
    hand = PSHandAddCard(hand, [PSCard cardWithSuit:kSuitSpades andRank:kRankQueen]);
    hand = PSHandAddCard(hand, [PSCard cardWithSuit:kSuitDiamonds andRank:kRankQueen]);
    hand = PSHandAddCard(hand, [PSCard cardWithSuit:kSuitHearts andRank:kRankQueen]);
    XCTAssertEqual(6, PSHandGetSize(hand));
    hand = PSHandRemoveSuit(hand, kSuitClubs);
    XCTAssertEqual(3, PSHandGetSize(hand));
}

-(void)testRemoveCards {
    PSHandHash hand = PSHandAddCard(0, [PSCard cardWithSuit:kSuitClubs andRank:kRankSeven]);
    PSCard *jackOfClubs = [PSCard cardWithSuit:kSuitClubs andRank:kRankJack];
    hand = PSHandAddCard(hand, jackOfClubs);
    PSCard *queenOfClubs = [PSCard cardWithSuit:kSuitClubs andRank:kRankQueen];
    hand = PSHandAddCard(hand, queenOfClubs);
    PSCard *queenOfSpades = [PSCard cardWithSuit:kSuitSpades andRank:kRankQueen];
    hand = PSHandAddCard(hand, queenOfSpades);
    hand = PSHandAddCard(hand, [PSCard cardWithSuit:kSuitDiamonds andRank:kRankQueen]);
    hand = PSHandAddCard(hand, [PSCard cardWithSuit:kSuitHearts andRank:kRankQueen]);
    XCTAssertEqual(6, PSHandGetSize(hand));
    PSCardHash toRemove[3] = {queenOfClubs.hash32, jackOfClubs.hash32, queenOfSpades.hash32};
    hand = PSHandRemoveCards(hand, toRemove, 3);
    XCTAssertEqual(3, PSHandGetSize(hand));
}

-(void)testContainsCard {
    PSHandHash hand = PSHandAddCard(0, [PSCard cardWithSuit:kSuitClubs andRank:kRankSeven]);
    PSCard *jackOfClubs = [PSCard cardWithSuit:kSuitClubs andRank:kRankJack];
    hand = PSHandAddCard(hand, jackOfClubs);
    PSCard *queenOfClubs = [PSCard cardWithSuit:kSuitClubs andRank:kRankQueen];
    hand = PSHandAddCard(hand, queenOfClubs);
    PSCard *queenOfSpades = [PSCard cardWithSuit:kSuitSpades andRank:kRankQueen];
    hand = PSHandAddCard(hand, queenOfSpades);
    XCTAssertTrue(PSHandContainsCard(hand, jackOfClubs.hash32));
    XCTAssertTrue(PSHandContainsCard(hand, queenOfClubs.hash32));
    XCTAssertTrue(PSHandContainsCard(hand, queenOfSpades.hash32));
    XCTAssertFalse(PSHandContainsCard(hand, [PSCard cardWithSuit:kSuitDiamonds andRank:kRankAce].hash32));
}

-(void)testContainsAllCards {
    PSHandHash hand = PSHandAddCard(0, [PSCard cardWithSuit:kSuitClubs andRank:kRankSeven]);
    PSCard *jackOfClubs = [PSCard cardWithSuit:kSuitClubs andRank:kRankJack];
    hand = PSHandAddCard(hand, jackOfClubs);
    PSCard *queenOfClubs = [PSCard cardWithSuit:kSuitClubs andRank:kRankQueen];
    hand = PSHandAddCard(hand, queenOfClubs);
    PSHandHash other = hand;
    PSCard *queenOfSpades = [PSCard cardWithSuit:kSuitSpades andRank:kRankQueen];
    hand = PSHandAddCard(hand, queenOfSpades);
    XCTAssertTrue(PSHandContainsAllCards(hand, other));
    XCTAssertFalse(PSHandContainsAllCards(other, hand));
}

-(void)testAdjustForDrop {
    PSCardHash cards[] = {
        [PSCard cardWithSuit:kSuitSpades andRank:kRankSeven].hash32
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankNine].hash32
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankJack].hash32
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankQueen].hash32
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankKing].hash32
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankAce].hash32
    ,   [PSCard cardWithSuit:kSuitClubs andRank:kRankNine].hash32
    ,   [PSCard cardWithSuit:kSuitClubs andRank:kRankTen].hash32
    ,   [PSCard cardWithSuit:kSuitClubs andRank:kRankAce].hash32
    ,   [PSCard cardWithSuit:kSuitDiamonds andRank:kRankAce].hash32
    };
    PSHandHash hand = PSHandAddCards(0, cards, 10);
    XCTAssertEqual(10, PSHandGetSize(hand));
    PSCardHash talon[] = {
        [PSCard cardWithSuit:kSuitClubs andRank:kRankSeven].hash32
    ,   [PSCard cardWithSuit:kSuitHearts andRank:kRankAce].hash32
    };
    PSCardHash drop[] = {
        [PSCard cardWithSuit:kSuitClubs andRank:kRankSeven].hash32
    ,   [PSCard cardWithSuit:kSuitClubs andRank:kRankNine].hash32
    };
    hand = PSHandAdjust(hand, talon, drop);
    XCTAssertTrue(PSHandContainsCard(hand, [PSCard cardWithSuit:kSuitSpades andRank:kRankSeven].hash32));
    XCTAssertTrue(PSHandContainsCard(hand, [PSCard cardWithSuit:kSuitSpades andRank:kRankNine].hash32));
    XCTAssertTrue(PSHandContainsCard(hand, [PSCard cardWithSuit:kSuitSpades andRank:kRankJack].hash32));
    XCTAssertTrue(PSHandContainsCard(hand, [PSCard cardWithSuit:kSuitSpades andRank:kRankQueen].hash32));
    XCTAssertTrue(PSHandContainsCard(hand, [PSCard cardWithSuit:kSuitSpades andRank:kRankKing].hash32));
    XCTAssertTrue(PSHandContainsCard(hand, [PSCard cardWithSuit:kSuitSpades andRank:kRankAce].hash32));
    XCTAssertTrue(PSHandContainsCard(hand, [PSCard cardWithSuit:kSuitClubs andRank:kRankTen].hash32));
    XCTAssertTrue(PSHandContainsCard(hand, [PSCard cardWithSuit:kSuitClubs andRank:kRankAce].hash32));
    XCTAssertTrue(PSHandContainsCard(hand, [PSCard cardWithSuit:kSuitDiamonds andRank:kRankAce].hash32));
    XCTAssertTrue(PSHandContainsCard(hand, [PSCard cardWithSuit:kSuitHearts andRank:kRankAce].hash32));
}

-(void)testGetFirstCardNonEmpty {
    PSCardHash cards[] = {
        [PSCard cardWithSuit:kSuitSpades andRank:kRankSeven].hash32
    ,   [PSCard cardWithSuit:kSuitDiamonds andRank:kRankAce].hash32
    };
    PSHandHash hand = PSHandAddCards(0, cards, 2);
    XCTAssertEqual(cards[0], PSHandGetFirstCard(hand));
}

-(void)testGetFirstCardEmpty {
    XCTAssertEqual(0, PSHandGetFirstCard(0));
}

-(void)testGetLastCardNonEmpty {
    PSCardHash cards[] = {
        [PSCard cardWithSuit:kSuitSpades andRank:kRankSeven].hash32
    ,   [PSCard cardWithSuit:kSuitDiamonds andRank:kRankAce].hash32
    };
    PSHandHash hand = PSHandAddCards(0, cards, 2);
    XCTAssertEqual(cards[1], PSHandGetLastCard(hand));
}

-(void)testGetLastCardEmpty {
    XCTAssertEqual(0, PSHandGetLastCard(0));
}

-(void)testGetNext {
    PSCardHash cards[] = {
        [PSCard cardWithSuit:kSuitSpades andRank:kRankAce].hash32
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankKing].hash32
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankQueen].hash32
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankJack].hash32
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankNine].hash32
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankSeven].hash32
    ,   [PSCard cardWithSuit:kSuitClubs andRank:kRankAce].hash32
    ,   [PSCard cardWithSuit:kSuitClubs andRank:kRankTen].hash32
    ,   [PSCard cardWithSuit:kSuitClubs andRank:kRankNine].hash32
    ,   [PSCard cardWithSuit:kSuitDiamonds andRank:kRankAce].hash32
    };
    PSHandHash hand = PSHandAddCards(0, cards, 10);
    for (int i = 0 ; i != 9 ; i++) {
        XCTAssertEqual(cards[i+1], PSHandGetNextCard(hand, cards[i]));
    }
    XCTAssertEqual(0, PSHandGetNextCard(hand, cards[9]));
}

-(void)testGetPrevious {
    PSCardHash cards[] = {
        [PSCard cardWithSuit:kSuitSpades andRank:kRankAce].hash32
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankKing].hash32
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankQueen].hash32
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankJack].hash32
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankNine].hash32
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankSeven].hash32
    ,   [PSCard cardWithSuit:kSuitClubs andRank:kRankAce].hash32
    ,   [PSCard cardWithSuit:kSuitClubs andRank:kRankTen].hash32
    ,   [PSCard cardWithSuit:kSuitClubs andRank:kRankNine].hash32
    ,   [PSCard cardWithSuit:kSuitDiamonds andRank:kRankAce].hash32
    };
    PSHandHash hand = PSHandAddCards(0, cards, 10);
    for (int i = 1 ; i != 10 ; i++) {
        XCTAssertEqual(cards[i-1], PSHandGetPreviousCard(hand, cards[i]));
    }
    XCTAssertEqual(0, PSHandGetPreviousCard(hand, cards[0]));
}

-(void)testGetSize {
    PSHandHash res = PSHandAddCard(0, [PSCard cardWithSuit:kSuitClubs andRank:kRankSeven]);
    res = PSHandAddCard(res, [PSCard cardWithSuit:kSuitClubs andRank:kRankJack]);
    res = PSHandAddCard(res, [PSCard cardWithSuit:kSuitClubs andRank:kRankQueen]);
    XCTAssertEqual(3, PSHandGetSize(res));
}

-(void)testSuitCount {
    PSCardHash cards[] = {
        [PSCard cardWithSuit:kSuitSpades andRank:kRankAce].hash32
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankKing].hash32
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankSeven].hash32
    ,   [PSCard cardWithSuit:kSuitClubs andRank:kRankAce].hash32
    ,   [PSCard cardWithSuit:kSuitClubs andRank:kRankNine].hash32
    ,   [PSCard cardWithSuit:kSuitDiamonds andRank:kRankAce].hash32
    };
    PSHandHash hand = PSHandAddCards(0, cards, 6);
    XCTAssertEqual(3, PSHandSuitCount(hand));
    hand = PSHandAddCard(hand, [PSCard cardWithSuit:kSuitHearts andRank:kRankAce]);
    XCTAssertEqual(4, PSHandSuitCount(hand));
    XCTAssertEqual(0, PSHandSuitCount(0));
}

// PSTrickAddToTrash

-(void)testTrickAddToTrashWithTalon {
    PSCard *c1 = [PSCard cardWithSuit:kSuitSpades andRank:kRankKing];
    PSCard *c2 = [PSCard cardWithSuit:kSuitDiamonds andRank:kRankSeven];
    PSCard *c3 = [PSCard cardWithSuit:kSuitHearts andRank:kRankAce];
    PSCard *t = [PSCard cardWithSuit:kSuitClubs andRank:kRankAce];
    PSTrickHash trick = PSTrickAddCardTalon(0, t.compressed);
    trick = PSTrickAddCardPlayer(trick, 0, c1.compressed);
    trick = PSTrickAddCardPlayer(trick, 1, c2.compressed);
    trick = PSTrickAddCardPlayer(trick, 2, c3.compressed);
    PSHandHash hand = PSHandAddTrick(0, trick);
    XCTAssertTrue(PSHandContainsCard(hand, t.hash32));
    XCTAssertTrue(PSHandContainsCard(hand, c1.hash32));
    XCTAssertTrue(PSHandContainsCard(hand, c2.hash32));
    XCTAssertTrue(PSHandContainsCard(hand, c3.hash32));
}

-(void)testTrickAddToTrashNoTalon {
    PSCard *c1 = [PSCard cardWithSuit:kSuitSpades andRank:kRankKing];
    PSCard *c2 = [PSCard cardWithSuit:kSuitDiamonds andRank:kRankSeven];
    PSCard *c3 = [PSCard cardWithSuit:kSuitHearts andRank:kRankAce];
    PSTrickHash trick = PSTrickAddCardPlayer(0, 0, c1.compressed);
    trick = PSTrickAddCardPlayer(trick, 1, c2.compressed);
    trick = PSTrickAddCardPlayer(trick, 2, c3.compressed);
    PSHandHash hand = PSHandAddTrick(0, trick);
    XCTAssertTrue(PSHandContainsCard(hand, c1.hash32));
    XCTAssertTrue(PSHandContainsCard(hand, c2.hash32));
    XCTAssertTrue(PSHandContainsCard(hand, c3.hash32));
}

-(void)testHandDescription {
    PSCardHash cards[] = {
        [PSCard cardWithSuit:kSuitSpades andRank:kRankAce].hash32
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankKing].hash32
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankQueen].hash32
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankJack].hash32
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankNine].hash32
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankSeven].hash32
    ,   [PSCard cardWithSuit:kSuitClubs andRank:kRankAce].hash32
    ,   [PSCard cardWithSuit:kSuitClubs andRank:kRankTen].hash32
    ,   [PSCard cardWithSuit:kSuitClubs andRank:kRankNine].hash32
    ,   [PSCard cardWithSuit:kSuitDiamonds andRank:kRankAce].hash32
    };
    PSHandHash hand = PSHandAddCards(0, cards, 10);
    XCTAssertEqualObjects(@"♠: A, K, Q, J, 9, 7 ♣: A, 10, 9 ♦: A", PSHandDescription(hand));
}

-(void)testOrderedSuitsByColor {
    char *suits[] = {
        // This encodes all combinations of card suits
        "", "S", "C", "D", "H", "SC", "SD", "SH", "CD", "CH", "DH", "SDC", "SHC", "DSH", "DCH", "SDCH"
    };
    for (int i = 0 ; i != sizeof(suits)/sizeof(*suits) ; i++) {
        size_t len = strlen(suits[i]);
        NSMutableArray *expected = [NSMutableArray arrayWithCapacity:len];
        PSHandHash hand = 0;
        for (int j = 0 ; j != len ; j++) {
            PSCardSuit suit;
            switch (suits[i][j]) {
                case 'S': suit = kSuitSpades; break;
                case 'C': suit = kSuitClubs; break;
                case 'D': suit = kSuitDiamonds; break;
                case 'H': suit = kSuitHearts; break;
            }
            [expected addObject:@(suit)];
            hand = PSHandAddCard(hand, [PSCard cardWithSuit:suit andRank:kRankAce]);
        }
        NSArray *res = PSHandOrderedSuits(hand, kSuitOrderByColor);
        XCTAssertEqualObjects(expected, res);
    }
}

-(void)testOrderedSuitsByRank {
    NSArray *ordered = PSHandOrderedSuits(0, kSuitOrderByRank);
    XCTAssertEqual(4, ordered.count);
    for (int i = 0 ; i != 4 ; i++) {
        XCTAssertEqualObjects(@(PSCardSuitRising[i]), ordered[i]);
    }
}

-(void)testLowestCard {
    PSHandHash hand = 0;
    for (int i = 0 ; i != PS_CARD_SUIT_RISING_LEN ; i++) {
        PSCardSuit suit = PSCardSuitRising[i];
        XCTAssertEqual(kRankNull, PSCardSetLowestRank(PSHandGetSuit(hand, suit)));
        for (int j = 0 ; j != PS_CARD_RANK_COUNT ; j++) {
            PSCardRank rank = PSCardRankFalling[j];
            hand = PSHandAddCard(hand, [PSCard cardWithSuit:suit andRank:rank]);
            XCTAssertEqual(rank, PSCardSetLowestRank(PSHandGetSuit(hand, suit)));
        }
    }
}

-(void)testGetCards {
    NSArray *cards = @[
        [PSCard cardWithSuit:kSuitSpades andRank:kRankAce]
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankKing]
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankQueen]
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankJack]
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankNine]
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankSeven]
    ,   [PSCard cardWithSuit:kSuitClubs andRank:kRankAce]
    ,   [PSCard cardWithSuit:kSuitClubs andRank:kRankTen]
    ,   [PSCard cardWithSuit:kSuitClubs andRank:kRankNine]
    ,   [PSCard cardWithSuit:kSuitDiamonds andRank:kRankAce]
    ];
    PSHandHash hand = 0;
    for (PSCard *card in cards) {
        hand = PSHandAddCard(hand, card);
    }
    NSArray *allSuits = @[@(kSuitSpades),@(kSuitClubs),@(kSuitDiamonds),@(kSuitHearts)];
    NSArray *res = PSHandGetCards(hand, allSuits, kRankOrderDescending, YES);
    XCTAssertEqualObjects(cards, res);
    res = PSHandGetCards(hand, allSuits, kRankOrderDescending, NO);
    for (PSCard *card in cards) {
        XCTAssertTrue([res containsObject:card]);
    }
}

-(void)testMoveCandidates {
    PSCardHash cards[] = {
        [PSCard cardWithSuit:kSuitSpades andRank:kRankAce].hash32
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankKing].hash32
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankQueen].hash32
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankJack].hash32
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankNine].hash32
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankSeven].hash32
    ,   [PSCard cardWithSuit:kSuitClubs andRank:kRankAce].hash32
    ,   [PSCard cardWithSuit:kSuitClubs andRank:kRankTen].hash32
    ,   [PSCard cardWithSuit:kSuitClubs andRank:kRankNine].hash32
    ,   [PSCard cardWithSuit:kSuitDiamonds andRank:kRankAce].hash32
    };
    PSHandHash hand = PSHandAddCards(0, cards, 10);
    PSHandHash cand = PSHandMoveCandidates(hand, 0, kSuitNone);
    XCTAssertEqual(10, PSHandGetSize(cand));
    PSTrickHash trick = PSTrickAddCardTalon(0, [PSCard cardWithSuit:kSuitSpades andRank:kRankEight].compressed);
    cand = PSHandMoveCandidates(hand, trick, kSuitSpades);
    XCTAssertEqual(6, PSHandGetSize(cand));
    trick = PSTrickAddCardTalon(0, [PSCard cardWithSuit:kSuitClubs andRank:kRankEight].compressed);
    cand = PSHandMoveCandidates(hand, trick, kSuitClubs);
    XCTAssertEqual(3, PSHandGetSize(cand));
    trick = PSTrickAddCardTalon(0, [PSCard cardWithSuit:kSuitDiamonds andRank:kRankEight].compressed);
    cand = PSHandMoveCandidates(hand, trick, kSuitDiamonds);
    XCTAssertEqual(1, PSHandGetSize(cand));
    cand = PSHandMoveCandidates(hand, 0, kSuitHearts);
    XCTAssertEqual(10, PSHandGetSize(cand));
    trick = PSTrickAddCardTalon(0, [PSCard cardWithSuit:kSuitHearts andRank:kRankEight].compressed);
    cand = PSHandMoveCandidates(hand, trick, kSuitSpades);
    XCTAssertEqual(6, PSHandGetSize(cand));
    cand = PSHandMoveCandidates(hand, trick, kSuitClubs);
    XCTAssertEqual(3, PSHandGetSize(cand));
    cand = PSHandMoveCandidates(hand, trick, kSuitDiamonds);
    XCTAssertEqual(1, PSHandGetSize(cand));
}

-(void)testMoveCandidatesInGame {
    PSCardHash cards[] = {
        [PSCard cardWithSuit:kSuitSpades andRank:kRankAce].hash32
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankKing].hash32
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankQueen].hash32
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankJack].hash32
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankNine].hash32
    ,   [PSCard cardWithSuit:kSuitSpades andRank:kRankSeven].hash32
    ,   [PSCard cardWithSuit:kSuitClubs andRank:kRankAce].hash32
    ,   [PSCard cardWithSuit:kSuitClubs andRank:kRankTen].hash32
    ,   [PSCard cardWithSuit:kSuitClubs andRank:kRankNine].hash32
    ,   [PSCard cardWithSuit:kSuitDiamonds andRank:kRankAce].hash32
    };
    PSHandHash hand = PSHandAddCards(0, cards, 10);
    PSGame *game = [[PSGame alloc] init];
    PSHandHash cand = PSHandMoveCandidatesInGame(game, hand);
    XCTAssertEqual(10, PSHandGetSize(cand));
    game.trick.current = PSTrickAddCardTalon(0, [PSCard cardWithSuit:kSuitSpades andRank:kRankEight].compressed);
    [game setBid:[PSBid bidPlayWithTrump:kSuitSpades tricks:6]
       forPlayer:0
         withMax:[PSBid bidPlayWithTrump:kSuitSpades tricks:6]
    ];
    [game.player setDeclarer:0];
    cand = PSHandMoveCandidatesInGame(game, hand);
    XCTAssertEqual(6, PSHandGetSize(cand));
    game.trick.current = PSTrickAddCardTalon(0, [PSCard cardWithSuit:kSuitClubs andRank:kRankEight].compressed);
    [game setBid:[PSBid bidPlayWithTrump:kSuitClubs tricks:6]
       forPlayer:0
         withMax:[PSBid bidPlayWithTrump:kSuitSpades tricks:6]
    ];
    cand = PSHandMoveCandidatesInGame(game, hand);
    XCTAssertEqual(3, PSHandGetSize(cand));
    game.trick.current = PSTrickAddCardTalon(0, [PSCard cardWithSuit:kSuitDiamonds andRank:kRankEight].compressed);
    [game setBid:[PSBid bidPlayWithTrump:kSuitDiamonds tricks:6]
       forPlayer:0
         withMax:[PSBid bidPlayWithTrump:kSuitSpades tricks:6]
    ];
    cand = PSHandMoveCandidatesInGame(game, hand);
    XCTAssertEqual(1, PSHandGetSize(cand));
    [game setBid:[PSBid bidPlayWithTrump:kSuitHearts tricks:6]
       forPlayer:0
         withMax:[PSBid bidPlayWithTrump:kSuitSpades tricks:6]
    ];
    game.trick.current = 0;
    cand = PSHandMoveCandidatesInGame(game, hand);
    XCTAssertEqual(10, PSHandGetSize(cand));
    game.trick.current = PSTrickAddCardTalon(0, [PSCard cardWithSuit:kSuitHearts andRank:kRankEight].compressed);
    [game setBid:[PSBid bidPlayWithTrump:kSuitSpades tricks:6]
       forPlayer:0
         withMax:[PSBid bidPlayWithTrump:kSuitSpades tricks:6]
    ];
    cand = PSHandMoveCandidatesInGame(game, hand);
    XCTAssertEqual(6, PSHandGetSize(cand));
    [game setBid:[PSBid bidPlayWithTrump:kSuitClubs tricks:6]
       forPlayer:0
         withMax:[PSBid bidPlayWithTrump:kSuitSpades tricks:6]
    ];
    cand = PSHandMoveCandidatesInGame(game, hand);
    XCTAssertEqual(3, PSHandGetSize(cand));
    [game setBid:[PSBid bidPlayWithTrump:kSuitDiamonds tricks:6]
       forPlayer:0
         withMax:[PSBid bidPlayWithTrump:kSuitSpades tricks:6]
    ];
    cand = PSHandMoveCandidatesInGame(game, hand);
    XCTAssertEqual(1, PSHandGetSize(cand));
}

@end
